
package org.movee.spring.data.hugegraph.gremlin;

import org.movee.spring.data.hugegraph.annotation.EdgeHead;
import org.movee.spring.data.hugegraph.annotation.EdgeTail;
import org.movee.spring.data.hugegraph.annotation.Index;
import org.movee.spring.data.hugegraph.mapping.HugeEntityType;
import org.movee.spring.data.hugegraph.mapping.HugePersistentEntity;
import org.movee.spring.data.hugegraph.mapping.HugePersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.context.MappingContext;

public class BasicCrudGremlinTraversalCreator implements CrudGremlinTraversalCreator {

    private static final String GREMLIN_GRAPH = "g";

    private final MappingContext<? extends HugePersistentEntity<?>, HugePersistentProperty> mappingContext;

    public BasicCrudGremlinTraversalCreator(
            MappingContext<? extends HugePersistentEntity<?>, HugePersistentProperty> mappingContext) {
        this.mappingContext = mappingContext;
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> GremlinTraversal save(final T entity, final Class<T> entityClass) {

        HugePersistentEntity<T> persistentEntity = (HugePersistentEntity<T>)
                mappingContext.getPersistentEntity(entityClass);

        GremlinTraversal.Builder builder = GremlinTraversal.builder(GREMLIN_GRAPH);
        if (persistentEntity.getEntityType() == HugeEntityType.VERTEX) {
            if (persistentEntity.isNew(entity)) {
                builder.step(".addV('%s')", persistentEntity.getLabel());
            } else {
                builder.stepQuote(".V(%s)", persistentEntity.getId(entity));
            }
        } else if (persistentEntity.getEntityType() == HugeEntityType.EDGE) {
            if (persistentEntity.isNew(entity)) {

                PersistentPropertyAccessor propertyAccessor = persistentEntity.getPropertyAccessor(entity);
                HugePersistentProperty headPropterty = persistentEntity.getPersistentProperty(EdgeHead.class);
                HugePersistentEntity headEntity = mappingContext.getPersistentEntity(headPropterty.getActualType());

                HugePersistentProperty tailPropterty = persistentEntity.getPersistentProperty(EdgeTail.class);
                HugePersistentEntity tailEntity = mappingContext.getPersistentEntity(tailPropterty.getActualType());

                builder.step(".V(%s)", headEntity.getId(propertyAccessor.getProperty(headPropterty)))
                        .step(".as('from')")
                        .step(".V(%s)", tailEntity.getId(propertyAccessor.getProperty(tailPropterty)))
                        .step(".as('to')")
                        .step(".addE('%s')", persistentEntity.getLabel())
                        .step(".from('from')")
                        .step(".to('to')");
            } else {
                builder.stepQuote(".E(%s)", persistentEntity.getId(entity));
            }
        }

        PersistentPropertyAccessor propertyAccessor = persistentEntity.getPropertyAccessor(entity);
        for (HugePersistentProperty property : persistentEntity.getPersistentProperties()) {
            if (!property.isIdProperty() && !property.isEdgeHeadProperty() && !property.isEdgeTailProperty()) {
                if (persistentEntity.isNew(entity)) {
                    builder.stepProperty(property.getName(), propertyAccessor.getProperty(property));
                } else {
                    // unique index can not be modified
                    Index indexAnno = property.findAnnotation(Index.class);
                    if (indexAnno == null || !indexAnno.unique()) {
                        builder.stepProperty(property.getName(), propertyAccessor.getProperty(property));
                    }
                }
            }
        }

        return builder.build();
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> GremlinTraversal findById(final Object id, final Class<T> entityClass) {

        HugePersistentEntity<T> persistentEntity = (HugePersistentEntity<T>)
                mappingContext.getPersistentEntity(entityClass);

        GremlinTraversal.Builder builder = startTraversal(persistentEntity);
        return builder.stepQuote(".hasId(%s)", id)
                        .build();
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> GremlinTraversal findAll(final Class<T> entityClass) {

        HugePersistentEntity<T> persistentEntity = (HugePersistentEntity<T>)
                mappingContext.getPersistentEntity(entityClass);

        GremlinTraversal.Builder builder = startTraversal(persistentEntity);
        return builder.step(".hasLabel('%s')", persistentEntity.getLabel())
                        .build();
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> GremlinTraversal existsById(final Object id, final Class<T> entityClass) {
        HugePersistentEntity<T> persistentEntity = (HugePersistentEntity<T>)
                mappingContext.getPersistentEntity(entityClass);

        GremlinTraversal.Builder builder = startTraversal(persistentEntity);
        return builder.stepQuote(".hasId(%s)", id)
                        .step(".hasNext()")
                        .build();
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> GremlinTraversal count(final Class<T> entityClass) {

        HugePersistentEntity<T> persistentEntity = (HugePersistentEntity<T>)
                mappingContext.getPersistentEntity(entityClass);

        GremlinTraversal.Builder builder = startTraversal(persistentEntity);
        return builder.step(".count()")
                        .build();
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> GremlinTraversal deleteById(final Object id, final Class<T> entityClass) {

        HugePersistentEntity<T> persistentEntity = (HugePersistentEntity<T>)
                mappingContext.getPersistentEntity(entityClass);

        GremlinTraversal.Builder builder = startTraversal(persistentEntity);
        return builder.stepQuote(".hasId(%s)", id)
                        .step(".drop()")
                        .build();
    }


    @Override
    @SuppressWarnings("unchecked")
    public <T> GremlinTraversal deleteAll(final Class<T> entityClass) {

        HugePersistentEntity<T> persistentEntity = (HugePersistentEntity<T>)
                mappingContext.getPersistentEntity(entityClass);

        GremlinTraversal.Builder builder = startTraversal(persistentEntity);
        return builder.step(".hasLabel('%s')", persistentEntity.getLabel())
                        .step(".drop()")
                        .build();
    }

    @SuppressWarnings("unchecked")
    private <T> GremlinTraversal.Builder startTraversal(HugePersistentEntity<T> persistentEntity) {

        GremlinTraversal.Builder builder = GremlinTraversal.builder(GREMLIN_GRAPH);
        if (persistentEntity.getEntityType() == HugeEntityType.VERTEX) {
            builder.stepQuote(".V()");
        } else if (persistentEntity.getEntityType() == HugeEntityType.EDGE) {
            builder.stepQuote(".E()");
        }

        return builder;
    }

}

